#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include "peer_thread.h"
#include "peer.h"
#include "tad.h"

/* VAR GLOBALES */
extern struct peer *hashmap_peers;
extern struct file *hashmap_sfiles;
extern struct file *hashmap_lfiles;
extern pthread_t peers[NUM_MAX_PEERS];
extern pthread_t tracker;
/****************/

void peer_thread_init(struct peer *p) {

  printf("Connecting to %s:%s...\n", p->ip, p->port);
  struct sockaddr_in serverSockAddr;
  long hostAddr;
  
  bzero(&serverSockAddr , sizeof(serverSockAddr));
  hostAddr = inet_addr(p->ip);
  
  if ((long)hostAddr != (long)-1)
    bcopy(&hostAddr,&serverSockAddr.sin_addr,sizeof(hostAddr));
  else {
      printf("Couldn't connect to peer %s:%s\n", p->ip, p->port);
      return;
  }
  
  serverSockAddr.sin_port = htons(atoi(p->port));
  serverSockAddr.sin_family = AF_INET;


  if ((p->fd = socket(AF_INET,SOCK_STREAM,0)) < 0)
  {
    printf("Couldn't connect to peer %s:%s\n", p->ip, p->port);
    return;
  }

  if(connect(p->fd, (struct sockaddr *)&serverSockAddr, sizeof(serverSockAddr)) < 0)
  {
    printf("Couldn't connect to peer %s:%s\n", p->ip, p->port);
    return;
  }

}


extern int listening_port;
void *peer_thread(void *peer) {
  struct peer *p = peer;
  
  char ppbuff[BUF_SIZE], ppcmd[BUF_SIZE];
  int ppready=0, ppreadc, ppoffset=0;
 
  printf("Entered in thread num = %d ! buffer size: %d B\n", p->thread_id, BUF_SIZE);
  
  if(p->fd==0)
    peer_thread_init(p);
  
 
  while(1) {
    /*********** ECRITURES *************/
    struct file_list *fl = p->leeched_files; // Je récupère les fichiers que je dois télécharger 
    while(fl) {
      sprintf(ppcmd, "interested %s ", fl->key);
      write(p->fd, ppcmd, strlen(ppcmd)); printf(ppcmd);
      fl = fl->next;

      break; // Ne demander que le premier
  }
  
    /************ LECTURES ************/
    
    READ(p->fd, ppbuff, ppready, ppreadc, ppoffset);
        
        if((strstr(ppbuff, "interested"))) {
          printf("Command interested read\n");
          
          READ(p->fd, ppbuff, ppready, ppreadc, ppoffset);
          char key[HASH_LENGTH+1];
          strcpy(key, ppbuff);

          printf("Looking for file %s\n", key);
          struct file *f = find_file(key, hashmap_sfiles);
          if(!f) f = find_file(key, hashmap_lfiles);
          
          if(f) {
            write(p->fd, "have ", strlen("have "));  printf("have ");
            write(p->fd, key, strlen(key));  printf(key);
            write(p->fd, " ", strlen(" "));  printf(" ");
            write(p->fd, f->buffermap, (f->file_size/f->piece_size)?f->file_size/f->piece_size:1);
            printf("<bmap>"); printf("\n");
          }
          else {
            printf("File %s not found\n", key);
          }
        }
        
        else if((strstr(ppbuff, "getpieces"))) {
          printf("Command getpieces read\n");
          
          READ(p->fd, ppbuff, ppready, ppreadc, ppoffset);
          char key[HASH_LENGTH+1];
          strcpy(key, ppbuff);

          
          READ(p->fd, ppbuff, ppready, ppreadc, ppoffset); // [          
          
          struct file *f = find_file(key, hashmap_sfiles);
          if(!f) f = find_file(key, hashmap_lfiles);
          
          if(f) {
            int piece, nb_pieces = 0, pieces[BUF_SIZE];
            char lbuff[BUF_SIZE];
            char *data = NULL;
            

            strcpy(lbuff, SHARING_PATH);
            strcat(lbuff, "/");
            strcat(lbuff, f->name);
            FILE *file = fopen(lbuff, "rb");
            if(file) {
              data = malloc(f->piece_size);
              
              do {
                READ(p->fd, ppbuff, ppready, ppreadc, ppoffset);
                piece = atoi(ppbuff);
                if(!piece)  continue; // Echapper la pièce 0

                pieces[nb_pieces++] = piece;
              } while(strcmp(ppbuff, "]"));
                
              write(p->fd, "data ", strlen("data ")); printf("data ");
              write(p->fd, key, strlen(key)); printf(key);
              write(p->fd, " [ ", strlen(" [ ")); printf(" [ ");
              for(piece=0; piece<nb_pieces; piece++) {
                 sprintf(lbuff, "%d", pieces[piece]);
                 write(p->fd, lbuff, strlen(lbuff)); printf(lbuff);
                 write(p->fd, " : ", 3); printf(" : ");
                 fseek(file, (piece-1)*f->piece_size, SEEK_SET);
                 fread(data, f->piece_size, 1, file);
                 write(p->fd, data, f->piece_size); printf("<binary>");
                 //write(p->fd, "plop", 4); printf("plop");
                 write(p->fd, " ", 1); printf(" ");
                    }

                write(p->fd, "] ", 2); printf("] ");
                fclose(file);
                free(data);
            }
            else
               printf("unable to open %s\n", lbuff);
          }
        }

        
        else if((strstr(ppbuff, "data"))) {
          printf("Command data read\n");
          
          READ(p->fd, ppbuff, ppready, ppreadc, ppoffset);
          char key[HASH_LENGTH+1];
          strcpy(key, ppbuff);
          //char *data;
          
          READ(p->fd, ppbuff, ppready, ppreadc, ppoffset); // [          
          
          struct file *f = find_file(key, hashmap_lfiles);
          
          if(f) {
            int piece = 0;
            //data = malloc(sizeof(f->piece_size));
            //long offset_data = 0;
            while(1) {
              READ(p->fd, ppbuff, ppready, ppreadc, ppoffset);
              if(!strcmp(ppbuff, "]")) break;
              
              piece = atoi(ppbuff);
              if(!piece)  continue; // Echapper la pièce 0

              printf("received piece %d\n", piece);
              READ(p->fd, ppbuff, ppready, ppreadc, ppoffset); // :

              READ(p->fd, ppbuff, ppready, ppreadc, ppoffset); // DATA
              //READ_DATA(p->fd, data, ppreadc, offset_data, f->piece_size); // READ_DATA(fd, buff, readc, offset, size)

              f->buffermap[(piece-1)/8] |= BIT_MASK << (piece%8 - 1);
              printf("New buffermap: %s\n", f->buffermap);
            }
            //free(data);
          }
          else
            printf("File %s has never been requested\n", key);
        }
        
        
        else if((strstr(ppbuff, "have"))) {

          printf("Command have read\n");
          int i, j; char c; long long d;
          int nb_pieces = 0, pieces[BUF_SIZE];
          
          READ(p->fd, ppbuff, ppready, ppreadc, ppoffset);
          char key[HASH_LENGTH+1];
          strcpy(key, ppbuff);
          
          struct file *f = find_file(key, hashmap_lfiles);
          
          if(f) {
            long bm_size = (f->file_size/f->piece_size)? f->file_size/f->piece_size:1;
            printf("BM size = %ld\n", bm_size);
            char *bmap = calloc(bm_size, 1);
            int bmreadc, bmoffset=0;
            READ_DATA(p->fd, bmap, bmreadc, bmoffset, bm_size) //#define READ_DATA(fd, buff, readc, offset, size)   
            //READ(p->fd, ppbuff, ppready, ppreadc, ppoffset);  // buffermap
            
            // On parcoure tous les caractères c du buffermap
            int bm = f->file_size/8 + (f->file_size%8?1:0);
            for(i=0; i<bm; i++) {
              c = ppbuff[i];
              for(j=0; j<8; j++) {
                d = ((BIT_MASK << j) & c);
                // if d is true then piece j+1 is necessary
                if(d) pieces[nb_pieces++] = j+1 + 8*(bm-i-1);
              }
            }
            
            sprintf(ppcmd, "getpieces %s [ ", key);
            write(p->fd, ppcmd, strlen(ppcmd)); printf(ppcmd);
            for(i=0; i<nb_pieces; i++) {
              sprintf(ppcmd, "%d ", pieces[i]);
              write(p->fd, ppcmd, strlen(ppcmd)); printf(ppcmd);
            }
            write(p->fd, "] ", 2); printf("] \n");
          }
          else
            printf("File %s has never been requested\n", key);
        }
        else {
          printf("'%s' unknown\n", ppbuff);
        }
        
        // else if ...
        
      }

  CHKN(close(p->fd));
  free(ppbuff);
  
  printf("Client %d is exiting\n", p->thread_id);
  return NULL;
}









